﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Office.Server;
using Microsoft.Office.Server.UserProfiles;
using Microsoft.SharePoint;
using System.Xml;

namespace AndersRask.SharePoint.Utilities
{
    public enum Operation
    {
        Import,
        Export,
        Unsupported
    }

    class MigrateUserProfileValues
    {
        static void Main(string[] args)
        {
            if (args.Length <= 2)
            {
                Console.WriteLine("MigrateUserProfileValues syntax:");
                Console.WriteLine("MigrateUserProfileValues.exe <url> <import|export> [filename]");
                Console.WriteLine("Example:");
                Console.WriteLine("MigrateUserProfileValues.exe http://moss2007 export sample.xml");
                Console.WriteLine("MigrateUserProfileValues.exe http://moss2007 import c:\\sample.xml");
                return;
            }

            string url = args[0];
            Operation operation = GetOperationMode(args[1]);
            string fileName = args[2];
            
            switch (operation)
            {
                case Operation.Unsupported:
                    throw new InvalidOperationException("You must specify Import or Export");
                    break;
                case Operation.Import:
                    ImportProfiles(url, fileName);
                    break;
                case Operation.Export:
                    ExportProfiles(url, fileName);
                    break;
                default:
                    break;
            }

            

        }

        /// <summary>
        /// Export user profile values.
        /// Writes out an xml document "export.xml" placed in current root
        /// </summary>
        /// <param name="url"></param>
        private static void ExportProfiles(string url, string fileName)
        {
            UserProfileManager profileManager = GetUserProfileManager(url);
            PropertyCollection profileProperties = profileManager.Properties;
            
            XmlDocument xmlDoc = new XmlDocument();
            XmlDeclaration xmlDeclaration = xmlDoc.CreateXmlDeclaration("1.0", "UTF-8", "yes");
            xmlDoc.AppendChild(xmlDeclaration);
            XmlNode userProfilesNode = xmlDoc.CreateElement("userprofiles");
            xmlDoc.AppendChild(userProfilesNode);

            foreach (UserProfile userProfile in profileManager)
            {
                XmlNode userProfileNode = xmlDoc.CreateElement("userprofile");
                userProfilesNode.AppendChild(userProfileNode);
                XmlAttribute userProfileAttribute;

                // username
                userProfileAttribute = xmlDoc.CreateAttribute("username");
                userProfileAttribute.Value = userProfile[PropertyConstants.UserName].Value.ToString();
                userProfileNode.Attributes.Append(userProfileAttribute);

                // accountname
                userProfileAttribute = xmlDoc.CreateAttribute("accountname");
                userProfileAttribute.Value = userProfile[PropertyConstants.AccountName].Value.ToString();
                userProfileNode.Attributes.Append(userProfileAttribute);

                foreach (Property property in profileProperties)
                {

                    if (property.IsUserEditable
                        && !property.IsSection)
                    {
                        if (userProfile[property.Name] == null || userProfile[property.Name].Value == null)
                        {
                            continue;
                        }

                        XmlNode propertyNode = xmlDoc.CreateElement("property");
                        userProfileNode.AppendChild(propertyNode);

                        XmlNode nameNode = xmlDoc.CreateElement("name");
                        propertyNode.AppendChild(nameNode);
                        nameNode.InnerXml = property.Name;                        
                        
                        XmlNode displayNameNode = xmlDoc.CreateElement("displayname");
                        propertyNode.AppendChild(displayNameNode);
                        displayNameNode.InnerXml = property.DisplayName;

                        XmlNode isChoiceListNode = xmlDoc.CreateElement("isChoiceList");
                        propertyNode.AppendChild(isChoiceListNode);
                        if ((property.ChoiceType == ChoiceTypes.Closed 
                            || property.ChoiceType == ChoiceTypes.Open)
                            && property.ChoiceList != null )
                        { 
                            isChoiceListNode.InnerXml = "True";
                        }
                        else
                        {
                            isChoiceListNode.InnerXml = "False";
                        }



                        XmlNode valueNode = xmlDoc.CreateElement("value");
                        propertyNode.AppendChild(valueNode);
                        XmlCDataSection valueCDataSection;
                        if (property.IsMultivalued)
                        {
                            UserProfileValueCollection userProfilevalues = userProfile[property.Name] as UserProfileValueCollection;
                            List<string> values = new List<string>(userProfilevalues.Count);
                            foreach (object value in userProfilevalues)
                            {
                                values.Add(value.ToString());
                            }

                            valueCDataSection = xmlDoc.CreateCDataSection(string.Join(",", values.ToArray()));
                        }
                        else
                        {
                            valueCDataSection = xmlDoc.CreateCDataSection(userProfile[property.Name].Value.ToString());
                        }
                        
                        
                        valueNode.AppendChild(valueCDataSection);
                        
                        XmlNode typeNode = xmlDoc.CreateElement("type");
                        propertyNode.AppendChild(typeNode);
                        typeNode.InnerXml = userProfile[property.Name].Value.GetType().ToString();

                    }
                }
                
            }

            xmlDoc.Save(fileName);
            
        }

        /// <summary>
        /// Import user profiles using specified xml file
        /// </summary>
        /// <example>
        ///   <userprofiles>
        ///     <userprofile preferredname="proactive\proadm" username="proadm" accountname="proactive\proadm">
        ///         <property>
        ///             <name>HomePhone</name> 
        ///             <displayname>Home phone</displayname> 
        ///             <value>12345678</value> 
        ///             <type>System.String</type> 
        ///         </property>
        ///     </userprofile>
        ///   </userprofiles>
        /// </example>
        /// <param name="url"></param>
        /// <param name="fileName"></param>
        private static void ImportProfiles(string url, string fileName)
        {
            UserProfileManager profileManager = GetUserProfileManager(url);
            PropertyCollection profileProperties = profileManager.Properties;

            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.Load(fileName);

            XmlNodeList userProfiles = xmlDoc.SelectNodes("userprofiles/userprofile");
            XmlNode userProfileInnerXml = null;
            UserProfile userProfile = null;
            string accountName = string.Empty;

            foreach (XmlNode userProfileNode in userProfiles)
            {
                //userProfileInnerXml = userProfileNode.SelectSingleNode("userprofile");
                accountName = userProfileNode.Attributes["accountname"].Value;

                if ( !profileManager.UserExists(accountName))
                { 
                    // TODO: LOG could not load user profile for accountname
                    continue;                
                }

                userProfile = profileManager.GetUserProfile(accountName);


                Console.WriteLine("Importing profile data for user {0}", userProfile[PropertyConstants.PreferredName]);

                foreach (XmlNode propertyNode in userProfileNode)
                {
                    string name = propertyNode.SelectSingleNode("./name").InnerXml;
                    string displayName = propertyNode.SelectSingleNode("./displayname").InnerXml;
                    bool isChoiceList = Boolean.Parse(propertyNode.SelectSingleNode("./isChoiceList").InnerXml);
                    string type = propertyNode.SelectSingleNode("./type").InnerXml;
                    XmlCDataSection valueNode = propertyNode.SelectSingleNode("./value").ChildNodes[0] as XmlCDataSection;
                    object value = valueNode.Value;
                    object convertedValue = Convert.ChangeType(value, Type.GetType(type));

                    if (isChoiceList)
                    {
                        List<string> values = new List<string>();
                        values.AddRange(convertedValue.ToString().Split(','));
                        foreach ( string val in values )
                        {
                            userProfile[name].Add(val);
                        }

                    }
                    else
                    {
                        userProfile[name].Add(convertedValue);
                    }
                    userProfile.Commit();

                    Console.WriteLine("Found field {0} with value {1} of type {2}", name, value, type);
                }
            }

            
        }

        private static UserProfileManager GetUserProfileManager(string url)
        {
 	        using (SPSite site = new SPSite(url))
            {
                ServerContext serverContext = ServerContext.GetContext(site);
                UserProfileManager profileManager = new UserProfileManager(serverContext);
                return profileManager;
            }
        }


        private static Operation GetOperationMode(string input)
        {
            Operation operation;
            switch (input)
            {
                case "import":
                    operation = Operation.Import;
                    break;
                case "export":
                    operation = Operation.Export;
                    break;
                default:
                    operation = Operation.Unsupported;
                    break;
            }
            return operation;
        }
    }
}
